﻿using System;
using System.Diagnostics.CodeAnalysis;
using System.Windows;
using System.Windows.Controls;
using YourLastOptionsDialog.Controls;
using YourLastOptionsDialog.ViewModels.Options;

namespace YourLastOptionsDialog.Views
{
    /// <summary>
    /// The editor for string values.
    /// </summary>
    public partial class PasswordEditor : UserControl
    {
        private string _originalPassword;

        /// <summary>
        /// Initializes a new instance of the <see cref="PasswordEditor"/> class.
        /// </summary>
        public PasswordEditor()
        {
            // Required to initialize variables
            InitializeComponent();
        }

        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Hooked in XAML.")]
        private void ValidatingPasswordBox_OnPasswordChanged(object sender, EventArgs e)
        {
            // perform the validation manually here instead of on the password box level.
            // in combination with the binding for the password, which is set up as one-way,
            // this allows us to explicitly synchronize the password back to the data level ONLY
            // if all our validations, including the confirmation checks, are performed correctly.
            var passwordOption = DataContext as PasswordOption;
            if (passwordOption == null)
            {
                return;
            }

            // backup
            if (_originalPassword == null)
            {
                _originalPassword = passwordOption.TextValue;
            }

            // validate
            var validationResult = passwordOption.Validator != null ? passwordOption.Validator.Validate(ValidatingPasswordBox.Password) : ValidationResult.Success;
            ValidatingPasswordBox.ValidationErrorMessage = validationResult.ValidationErrorMessage;
            ValidatingPasswordBox.IsValid = validationResult.IsValid;

            // confirmation check?
            if (passwordOption.ShowConfirmation)
            {
                if (!ValidatePasswordConfirmation())
                {
                    // make sure to reset (but only if different, to avoid an unnecessary change trigger in the data source)
                    if (passwordOption.TextValue != _originalPassword)
                    {
                        passwordOption.TextValue = _originalPassword;
                    }

                    return;
                }
            }

            // only sync if valid
            if (!validationResult.IsValid)
            {
                return;
            }

            // manually synchronize data (=> only if not showconfirmation == true && isConfirmationValid == false)
            SynchronizeDataSource(passwordOption);
        }

        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Hooked in XAML.")]
        private void ConfirmationValidatingPasswordBox_OnPasswordChanged(object sender, EventArgs e)
        {
            var passwordOption = DataContext as PasswordOption;
            if (passwordOption == null)
            {
                return;
            }

            if (!passwordOption.ShowConfirmation)
            {
                // this should never happen by user action
                ConfirmationValidatingPasswordBox.IsValid = true;
                ConfirmationValidatingPasswordBox.ValidationErrorMessageVisibility = Visibility.Collapsed;
                return;
            }

            // make sure to set the password confirmation visuals to the correct state
            if (ValidatePasswordConfirmation())
            {
                // check to see if we now can synchronize to the data source
                if (ValidatingPasswordBox.IsValid)
                {
                    SynchronizeDataSource(passwordOption);
                }
            }
        }

        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from an event handler that is hooked in XAML.")]
        private void SynchronizeDataSource(PasswordOption passwordOption)
        {
            passwordOption.TextValue = ValidatingPasswordBox.Password;
            passwordOption.ConfirmationPassword = ConfirmationValidatingPasswordBox.Password;

            // reset to new reference
            _originalPassword = null;
        }

        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from an event handler that is hooked in XAML.")]
        private bool ValidatePasswordConfirmation()
        {
            var isConfirmationValid = ValidatingPasswordBox.Password.Equals(ConfirmationValidatingPasswordBox.Password, StringComparison.Ordinal);

            // show the validation error
            ConfirmationValidatingPasswordBox.IsValid = isConfirmationValid;
            ConfirmationValidatingPasswordBox.ValidationErrorMessageVisibility = isConfirmationValid ? Visibility.Collapsed : Visibility.Visible;

            return isConfirmationValid;
        }
    }
}